// Copyright (c) 2010 SuccessFactors, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//     * Redistributions of source code must retain the above
//       copyright notice, this list of conditions and the following
//       disclaimer.
//
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials
//       provided with the distribution.
//
//     * Neither the name of the SuccessFactors, Inc. nor the names of
//       its contributors may be used to endorse or promote products
//       derived from this software without specific prior written
//       permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

package org.owasp.jxt.runtime;

import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.el.ExpressionEvaluator;
import javax.servlet.jsp.el.VariableResolver;

/**
 * JxtPageContext
 *
 * @author Jeffrey Ichnowski
 * @version $Revision: 8 $
 */
public class JxtPageContext extends PageContext {

    private Servlet _servlet;
    private ServletContext _servletContext;
    private ServletRequest _request;
    private ServletResponse _response;
    private HttpSession _session;

    private ServletConfig _servletConfig;

    private Map<String,Object> _pageAttributes = new HashMap<String,Object>();
    private JspWriter _out;

    /** Exception of the error (if this is an error page). */
    private Exception _exception;


    /** {@inheritDoc} */
    public final void initialize(
        final Servlet servlet,
        final ServletRequest request,
        final ServletResponse response,
        final String errorPageURL,
        final boolean needsSession,
        final int bufferSize,
        final boolean autoFlush)
        throws IOException // IllegalStateException, IllegalArgumentException
    {
        _servlet = servlet;
        _servletConfig = servlet.getServletConfig();
        _servletContext = _servletConfig.getServletContext();
        _request = request;
        _response = response;

        // TODO: errorPageURL (used in handlePageException later, I
        // believe)

        if (request instanceof HttpServletRequest) {
            _session = ((HttpServletRequest)request).getSession(needsSession);
        }

        // make sure this is the last statement.  getWriter is the
        // only thing that will throw an IOException, and the caller
        // of this JxtPageContext cannot pass on the exception because
        // of the contract spec.
        _out = new JxtJspWriter(response.getWriter(), bufferSize, autoFlush);
    }


    /** {@inheritDoc} */
    public final JspWriter getOut() {
        return _out;
    }

    /** {@inheritDoc} */
    public final int getAttributesScope(final String name) {
        if (name == null) {
            throw new NullPointerException();
        }
        if (_pageAttributes.containsKey(name)) {
            return PAGE_SCOPE;
        } else if (_request.getAttribute(name) != null) {
            return REQUEST_SCOPE;
        } else if (_session != null && _session.getAttribute(name) != null) {
            return SESSION_SCOPE;
        } else if (_servletContext.getAttribute(name) != null) {
            return APPLICATION_SCOPE;
        }
        return 0;
    }

    /** {@inheritDoc} */
    public final Enumeration getAttributeNamesInScope(final int scope) {
        switch (scope) {
        case PAGE_SCOPE:
            return Collections.enumeration(_pageAttributes.keySet());
        case REQUEST_SCOPE:
            return _request.getAttributeNames();
        case SESSION_SCOPE:
            if (_session == null) {
                throw new IllegalStateException("session not active");
            }
            return _session.getAttributeNames();
        case APPLICATION_SCOPE:
            return _servletContext.getAttributeNames();
        default:
            throw new IllegalArgumentException("invalid scope: "+scope);
        }
    }

    /** {@inheritDoc} */
    public final ExpressionEvaluator getExpressionEvaluator() {
        return null;
    }

    /** {@inheritDoc} */
    public final VariableResolver getVariableResolver() {
        return null;
    }

    /** {@inheritDoc} */
    public final Object getAttribute(final String name, final int scope) {
        if (name == null) {
            throw new NullPointerException("name is null");
        }

        switch (scope) {
        case PAGE_SCOPE:
            return _pageAttributes.get(name);
        case REQUEST_SCOPE:
            return _request.getAttribute(name);
        case SESSION_SCOPE:
            if (_session == null) {
                throw new IllegalStateException("session not active");
            }
            return _session.getAttribute(name);
        case APPLICATION_SCOPE:
            return _servletContext.getAttribute(name);
        default:
            throw new IllegalStateException("invalid scope: "+scope);
        }
    }

    /** {@inheritDoc} */
    public final Object getAttribute(final String name) {
        return _pageAttributes.get(name);
    }

    /** {@inheritDoc} */
    public final void removeAttribute(final String name, final int scope) {
        if (name == null) {
            throw new NullPointerException("name is null");
        }

        switch (scope) {
        case PAGE_SCOPE:
            _pageAttributes.remove(name);
            break;
        case REQUEST_SCOPE:
            _request.removeAttribute(name);
            break;
        case SESSION_SCOPE:
            if (_session == null) {
                throw new IllegalStateException("session not active");
            }
            _session.removeAttribute(name);
            break;
        case APPLICATION_SCOPE:
            _servletContext.removeAttribute(name);
            break;
        default:
            throw new IllegalStateException("invalid scope: "+scope);
        }
    }

    /** {@inheritDoc} */
    public final void removeAttribute(final String name) {
        if (name == null) {
            throw new NullPointerException("name is null");
        }

        _pageAttributes.remove(name);
        _request.removeAttribute(name);
        if (_session != null) {
            _session.removeAttribute(name);
        }
        _servletContext.removeAttribute(name);
    }

    /** {@inheritDoc} */
    public final void setAttribute(final String name, final Object value) {
        if (name == null) {
            throw new NullPointerException("name is null");
        }

        if (value == null) {
            _pageAttributes.remove(name);
        } else {
            _pageAttributes.put(name, value);
        }
    }

    /** {@inheritDoc} */
    public final void setAttribute(final String name, final Object value, final int scope) {
        if (name == null) {
            throw new NullPointerException("name is null");
        }

        if (value == null) {
            removeAttribute(name, scope);
        } else {
            switch (scope) {
            case PAGE_SCOPE:
                _pageAttributes.put(name, value);
                break;
            case REQUEST_SCOPE:
                _request.setAttribute(name, value);
                break;
            case SESSION_SCOPE:
                if (_session == null) {
                    throw new IllegalStateException("session not active");
                }
                _session.setAttribute(name, value);
                break;
            case APPLICATION_SCOPE:
                _servletContext.setAttribute(name, value);
                break;
            default:
                throw new IllegalStateException("invalid scope: "+scope);
            }
        }
    }

    /** {@inheritDoc} */
    public final Object findAttribute(final String name) {
        Object value = _pageAttributes.get(name);
        if (value != null) {
            return value;
        }

        value = _request.getAttribute(name);
        if (value != null) {
            return value;
        }

        if (_session != null) {
            value = _session.getAttribute(name);
            if (value != null) {
                return value;
            }
        }

        return _servletContext.getAttribute(name);
    }

    // Implementation of javax.servlet.jsp.PageContext

    /** {@inheritDoc} */
    public final ServletConfig getServletConfig() {
        return _servletConfig;
    }

    /** {@inheritDoc} */
    public final HttpSession getSession() {
        return _session;
    }

    /** {@inheritDoc} */
    public final ServletRequest getRequest() {
        return _request;
    }

    /** {@inheritDoc} */
    public final ServletResponse getResponse() {
        return _response;
    }

    /** {@inheritDoc} */
    public final ServletContext getServletContext() {
        return _servletContext;
    }

    /** {@inheritDoc} */
    public final void forward(final String relativeUrlPath) throws ServletException, IOException {
        // TODO!
    }

    /** {@inheritDoc} */
    public final void include(final String relativeUrlPath, final boolean flush) throws ServletException, IOException {
        // TODO
    }

    /** {@inheritDoc} */
    public final void include(final String relativeUrlPath) throws ServletException, IOException {
        // TODO
    }

    /** {@inheritDoc} */
    public final void handlePageException(final Throwable throwable) throws ServletException, IOException {
        // TODO
    }

    /** {@inheritDoc} */
    public final void handlePageException(final Exception exception) throws ServletException, IOException {
        // TODO
    }

    /** {@inheritDoc} */
    public final Exception getException() {
        return _exception;
    }


    /** {@inheritDoc} */
    public final void release() {

    }

    /** {@inheritDoc} */
    public final Object getPage() {
        return _servlet;
    }

} // JxtPageContext
